//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using OpenLS.Drawing;
using OpenLS.Spreadsheet.Drawing;

namespace OpenLS.Spreadsheet.UI
{

    public class ResizeAndRotateAdorner : Adorner
    {
        readonly VisualCollection _visualChildren;
        public ResizeAndRotateAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            AllowRotate = true;
            Resources.MergedDictionaries.Add(new ResizeAndRotateAdornerResources());

            _visualChildren = new VisualCollection(this);
            var c = new ContentControl
                        {
                            Template = ((ControlTemplate) FindResource("resizeAndRotateControl")),
                            DataContext = this
                        };
            _visualChildren.Add(c);
            AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDrag));
        }

        public bool AllowRotate { get; set; }

        public Size? ProportionSize { get; set; }

        static int GetDragImpact(HorizontalAlignment a)
        {
            switch (a)
            {
                case HorizontalAlignment.Left:
                    return -1;
                case HorizontalAlignment.Right:
                    return 1;
                default:
                    return 0;
            }
        }

        static int GetDragImpact(VerticalAlignment a)
        {
            switch (a)
            {
                case VerticalAlignment.Top:
                    return -1;
                case VerticalAlignment.Bottom:
                    return 1;
                default:
                    return 0;
            }
        }

        void OnDrag(object sender, DragDeltaEventArgs args)
        {
            var hitThumb = (Thumb) args.OriginalSource;
            if (hitThumb.Style == FindResource("rotateThumb"))
            {
                HandleRotate(sender, args);
                return;
            }
            double h = args.HorizontalChange;
            double v = args.VerticalChange;
            int horizontalImpact = GetDragImpact(hitThumb.HorizontalAlignment);
            int verticalImpact = GetDragImpact(hitThumb.VerticalAlignment);
            if (ProportionSize != null && horizontalImpact * verticalImpact != 0 && ProportionSize.Value.Height > 0 && ProportionSize.Value.Width > 0)
            {
                var thisSize = new Size(ActualWidth + horizontalImpact * h, ActualHeight + verticalImpact * v);
                if (thisSize.Width * ProportionSize.Value.Height < thisSize.Height * ProportionSize.Value.Width)
                {
                    double newWidth = thisSize.Height*ProportionSize.Value.Width/ProportionSize.Value.Height;
                    h = (newWidth  - ActualWidth)/ horizontalImpact;
                }
                else
                {
                    double newHeight = thisSize.Width * ProportionSize.Value.Height / ProportionSize.Value.Width;
                    v = (newHeight - ActualHeight) / verticalImpact;

                }
            }
            switch (hitThumb.HorizontalAlignment)
            {
                case HorizontalAlignment.Left:
                    RaiseEvent(new DragSideDeltaEventArgs(h, v, Dock.Left)); break;
                case HorizontalAlignment.Right:
                    RaiseEvent(new DragSideDeltaEventArgs(h, v, Dock.Right)); break;
            }
            switch (hitThumb.VerticalAlignment)
            {
                case VerticalAlignment.Top:
                    RaiseEvent(new DragSideDeltaEventArgs(h, v, Dock.Top)); break;
                case VerticalAlignment.Bottom:
                    RaiseEvent(new DragSideDeltaEventArgs(h, v, Dock.Bottom)); break;
            }
            return;
        }


        [Category("Behavior")]
        public event DragSideDeltaEventHandler DragSideDelta
        {
            add
            {
                AddHandler(ResizeAdorner.DragSideDeltaEvent, value);
            }
            remove
            {
                RemoveHandler(ResizeAdorner.DragSideDeltaEvent, value);
            }
        }

        const double RotateOffset = 20;
        void HandleRotate(object sender, DragDeltaEventArgs args)
        {
            var adornedElement = this.AdornedElement as FrameworkElement;
            var cp = adornedElement as ContentPresenter;
            var hitThumb = args.OriginalSource as Thumb;
            if (adornedElement == null || hitThumb == null) return;
            // Ensure that the Width and Height are properly initialized after the resize.
            var tt = adornedElement.RenderTransform as RotateTransform;
            if (tt == null)
            {
                tt = new RotateTransform
                         {
                             CenterX = (adornedElement.DesiredSize.Width/2),
                             CenterY = (adornedElement.DesiredSize.Height/2)
                         };
                adornedElement.RenderTransform = tt;
            }
            UpdateTransform();
            tt.Angle += Math.Atan2(args.HorizontalChange, adornedElement.DesiredSize.Height / 2 + RotateOffset - args.VerticalChange) * 180 / Math.PI;
            object o = cp.Content;
            Anchor a = o as Anchor;
            if (a != null)
            {
                var b = a.Content as ShapeBase;
                if (b != null)
                    b.RotationAngle = tt.Angle;
            }

            UpdateCursors();
            InvalidateArrange();
        }
        void UpdateTransform()
        {
            var adornedElement = AdornedElement as FrameworkElement;
            var tt = adornedElement.RenderTransform as RotateTransform;
            if (tt != null)
            {
                tt.CenterX = adornedElement.DesiredSize.Width / 2;
                tt.CenterY = adornedElement.DesiredSize.Height / 2;
            }
        }
        void UpdateCursors()
        {
            var adornedElement = AdornedElement as FrameworkElement;
            var tt = adornedElement.RenderTransform as RotateTransform;
            double angle = 0;
            if (tt != null)
            {
                angle = tt.Angle;
            }
            var cc = (ContentControl)_visualChildren[0];
            DependencyObject g = VisualTreeHelper.GetChild(cc, 0);
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(g); i++)
            {
                var e = VisualTreeHelper.GetChild(g, i) as FrameworkElement;
                if (!(e is Thumb))
                    continue;
                int add = 0;
                if (e.Style == FindResource("rotateThumb"))
                    continue;
                switch (e.HorizontalAlignment)
                {
                    case HorizontalAlignment.Left:

                        switch (e.VerticalAlignment)
                        {
                            case VerticalAlignment.Top: add = 1; break;
                            case VerticalAlignment.Center: add = 2; break;
                            case VerticalAlignment.Bottom: add = 3; break;
                        }
                        break;
                    case HorizontalAlignment.Center:
                        switch(e.VerticalAlignment)
                        {
                            case VerticalAlignment.Top: add = 0; break;
                            case VerticalAlignment.Bottom: add= 4; break;
                        }
                    break;
                    case HorizontalAlignment.Right:
                        switch (e.VerticalAlignment)
                        {
                            case VerticalAlignment.Top: add = 7; break;
                            case VerticalAlignment.Center :add = 6; break;
                            case VerticalAlignment.Bottom: add = 5; break;
                        }
                        break;
                }
                add -= (int)Math.Round(angle / 45) ;
                e.Cursor = GetCursor (add);
                }

            }

        
        static Cursor GetCursor(int i)
        {
            switch (((i % 4) + 4) % 4)
            {
                case 0: return Cursors.SizeNS;
                case 1: return Cursors.SizeNWSE;
                case 2: return Cursors.SizeWE;
                case 3: return Cursors.SizeNESW;
                default: throw new NotSupportedException();
            }
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement e in _visualChildren)
                e.Arrange(new Rect(new Point(0, 0), finalSize));
            return base.ArrangeOverride(finalSize);
        }
        // Override the VisualChildrenCount and GetVisualChild properties to interface with 
        // the adorner's visual collection.
        protected override int VisualChildrenCount { get {if (_visualChildren == null)
            return 0;
            return _visualChildren.Count;
    }
}
        protected override Visual GetVisualChild(int index) { return _visualChildren[index]; }
    }
}